package com.zshan.clinic.admin.service.medical;

import com.zshan.clinic.admin.constant.Constants;
import com.zshan.clinic.admin.constant.RedisKeyPrefix;
import com.zshan.clinic.admin.entity.medical.CallLargeMessage;
import com.zshan.clinic.admin.entity.vo.ClinicQueueVo;
import com.zshan.clinic.admin.entity.vo.QueueBoardVo;
import com.zshan.clinic.admin.mapper.medical.SpecClinicQueueMapper;
import com.zshan.clinic.common.constant.CommonConstant;
import com.zshan.clinic.common.enums.*;
import com.zshan.clinic.common.exception.BusinessFailException;
import com.zshan.clinic.common.util.json.JsonUtil;
import com.zshan.clinic.common.util.page.PageInfo;
import com.zshan.clinic.common.util.page.PageUtils;
import com.zshan.clinic.common.util.redis.RedisUtil;
import com.zshan.clinic.common.util.string.StringUtil;
import com.zshan.clinic.database.dao.ClinicQueueMapper;
import com.zshan.clinic.database.model.ClinicQueue;
import com.zshan.clinic.database.model.ClinicQueueExample;
import com.zshan.clinic.database.model.ClinicRegister;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
public class ClinicQueueService {

    @Autowired
    private ClinicQueueMapper clinicQueueMapper;

    @Autowired
    private SpecClinicQueueMapper specClinicQueueMapper;

    @Autowired
    private ClinicRegisterService registerService;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 获取门诊叫号大屏数据
     * @param clinicId
     * @return
     */
    public List<QueueBoardVo> getBoardList(Long clinicId) {
        return specClinicQueueMapper.getBoardList(clinicId,LocalDate.now(), VisitPeriodEnum.getPeriod(LocalTime.now()));
    }

    /**
     * 签到成功加入门诊叫号排队队列
     * @param register
     */
    @Transactional(rollbackFor = Exception.class)
    public void checkInAndEnqueue(ClinicRegister register) {
        log.info("自动签到添加门诊排队表:{}",register);
        if(RegChannelEnum.OUTPATIENT.getValue().equals(register.getRegChannel())){
            //如果是在门诊直接看诊，就不用叫号了
            return;
        }
        if(!PayStatusEnum.PAID.getValue().equals(register.getPayStatus())){
            throw new BusinessFailException("状态不允许");
        }
        //加入叫号表
        ClinicQueue clinicQueue = new ClinicQueue();
        BeanUtils.copyProperties(register,clinicQueue);
        clinicQueue.setQueueStatus(QueueStatusEnum.WAITING.getValue());
        addQueue(clinicQueue);
    }



    /**
     * 分页查询签到记录
     * @param patientName 患者姓名（模糊搜索）
     * @param pageNo 页码
     * @param pageSize 每页数量
     * @return 分页结果
     */
    public PageInfo<ClinicQueue> getQueueListByCondition(String patientName, Integer pageNo, Integer pageSize) {
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        if (StringUtil.isNotBlank(patientName)) {
            criteria.andPatientNameLike("%" + patientName + "%");
        }
        criteria.andIsDeleteEqualTo(YesOrNoEnum.NO.getValue());

        Long totalCount = clinicQueueMapper.countByExample(example);
        if (totalCount == null || totalCount == 0) {
            return PageInfo.build();
        }

        example.setOffset(PageUtils.getOffset(pageNo, pageSize));
        example.setLimit(pageSize);
        example.setOrderByClause("checkin_time desc");
        List<ClinicQueue> list = clinicQueueMapper.selectByExample(example);
        return new PageInfo<>(totalCount, list);
    }

    /**
     * 添加签到记录
     * @param clinicQueue 签到实体
     */
    @Transactional(rollbackFor = Exception.class)
    public void addQueue(ClinicQueue clinicQueue) {
        validateQueue(clinicQueue);
        clinicQueue.setCallCount(CommonConstant.DEFAULT_0);
        clinicQueue.setIsDelete(YesOrNoEnum.NO.getValue());
        clinicQueue.setIsEnabled(YesOrNoEnum.YES.getValue());
        clinicQueue.setWaitTime(new Date());
        clinicQueueMapper.insertSelective(clinicQueue);
        //刷新候诊队列
        refreshReadyQueue(clinicQueue.getClinicId(),clinicQueue.getVisitDate(),clinicQueue.getVisitPeriod(),clinicQueue.getDoctorId());
    }

    /**
     * 刷新候诊队列
     */
    @Transactional(rollbackFor = Exception.class)
    public void refreshReadyQueue(Long clinicId,LocalDate visitDate,String visitPeriod,Long doctorId) {
        Long readyCount = getReadyCount(clinicId,visitDate,visitPeriod,doctorId);
        if(readyCount >= CommonConstant.DEFAULT_2){
            return;
        }
        //获取等待队列中的第一个，然后将状态改为候诊中
        List<ClinicQueue> waitingList = getQueueWaitingList(clinicId,visitDate,visitPeriod,doctorId);
        if(CollectionUtils.isEmpty(waitingList)){
            return;
        }
        ClinicQueue clinicQueue = waitingList.get(0);
        clinicQueue.setQueueStatus(QueueStatusEnum.READY.getValue());
        clinicQueue.setReadyTime(new Date());
        updateQueue(clinicQueue);
    }

    /**
     * 查询完诊列表
     * @param clinicId
     * @param doctorId
     * @return
     */
    public List<ClinicQueueVo> getTreatedList(Long clinicId,Long doctorId){
        List<ClinicQueue> queueList = getQueueTreatedList(clinicId,LocalDate.now(),VisitPeriodEnum.getPeriod(LocalTime.now()),doctorId);
        if(CollectionUtils.isEmpty(queueList)){
            return null;
        }
        return queueList.stream().map(queue -> {
            ClinicQueueVo clinicQueueVo = new ClinicQueueVo();
            BeanUtils.copyProperties(queue, clinicQueueVo);
            return clinicQueueVo;
        }).collect(Collectors.toList());
    }

    /**
     * 获取等待中的排队列表
     * @param clinicId
     * @param doctorId
     * @return
     */
    public List<ClinicQueueVo> getWaitingList(Long clinicId,Long doctorId){
        List<ClinicQueue> queueList = getQueueWaitingList(clinicId,LocalDate.now(),VisitPeriodEnum.getPeriod(LocalTime.now()),doctorId);
        if(CollectionUtils.isEmpty(queueList)){
            return null;
        }
        return queueList.stream().map(queue -> {
            ClinicQueueVo clinicQueueVo = new ClinicQueueVo();
            BeanUtils.copyProperties(queue, clinicQueueVo);
            return clinicQueueVo;
        }).collect(Collectors.toList());
    }


    /**
     * 获取呼叫中的信息
     * @param clinicId
     * @param doctorId
     * @return
     */
    public ClinicQueueVo getCalled(Long clinicId, Long doctorId) {
        ClinicQueue clinicQueue = getQueueCalledInfo(clinicId,LocalDate.now(),VisitPeriodEnum.getPeriod(LocalTime.now()),doctorId);
        if(clinicQueue == null){
            return null;
        }
        ClinicQueueVo clinicQueueVo = new ClinicQueueVo();
        BeanUtils.copyProperties(clinicQueue,clinicQueueVo);
        return clinicQueueVo;
    }


    /**
     * 查询候诊中的列表
     * @param clinicId
     * @param doctorId
     * @return
     */
    public List<ClinicQueueVo> getReadyList(Long clinicId,Long doctorId){
        List<ClinicQueue> queueList = getQueueReadyList(clinicId,LocalDate.now(),VisitPeriodEnum.getPeriod(LocalTime.now()),doctorId);
        if(CollectionUtils.isEmpty(queueList)){
            return null;
        }
        return queueList.stream().map(queue -> {
            ClinicQueueVo clinicQueueVo = new ClinicQueueVo();
            BeanUtils.copyProperties(queue, clinicQueueVo);
            return clinicQueueVo;
        }).collect(Collectors.toList());
    }

    /**
     * 获取等待队列
     * @return
     */
    public List<ClinicQueue> getQueueTreatedList(Long clinicId,LocalDate visitDate,String visitPeriod,Long doctorId){
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        criteria.andClinicIdEqualTo(clinicId);
        criteria.andVisitDateEqualTo(visitDate);
//        criteria.andVisitPeriodEqualTo(visitPeriod);
        criteria.andDoctorIdEqualTo(doctorId);
        criteria.andQueueStatusEqualTo(QueueStatusEnum.TREATED.getValue());
        criteria.andIsDeleteEqualTo(YesOrNoEnum.NO.getValue());
        example.setOrderByClause("treated_time desc");
        return clinicQueueMapper.selectByExample(example);
    }

    /**
     * 获取等待队列
     * @return
     */
    public List<ClinicQueue> getQueueWaitingList(Long clinicId,LocalDate visitDate,String visitPeriod,Long doctorId){
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        criteria.andClinicIdEqualTo(clinicId);
        criteria.andVisitDateEqualTo(visitDate);
//        criteria.andVisitPeriodEqualTo(visitPeriod);
        criteria.andDoctorIdEqualTo(doctorId);
        criteria.andQueueStatusIn(QueueStatusEnum.getReadyList());
        criteria.andIsDeleteEqualTo(YesOrNoEnum.NO.getValue());
        example.setOrderByClause("queue_status asc,visit_seq_no asc");
        return clinicQueueMapper.selectByExample(example);
    }

    /**
     * 获取候诊中的队列
     * @param clinicId
     * @param visitDate
     * @param doctorId
     * @return
     */
    public List<ClinicQueue> getQueueReadyList(Long clinicId,LocalDate visitDate,String visitPeriod,Long doctorId){
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        criteria.andClinicIdEqualTo(clinicId);
        criteria.andVisitDateEqualTo(visitDate);
//        criteria.andVisitPeriodEqualTo(visitPeriod);
        criteria.andDoctorIdEqualTo(doctorId);
        criteria.andQueueStatusEqualTo(QueueStatusEnum.READY.getValue());
        criteria.andIsDeleteEqualTo(YesOrNoEnum.NO.getValue());
        example.setOrderByClause("ready_time asc");
        return clinicQueueMapper.selectByExample(example);
    }

    /**
     * 呼叫中的信息
     * @param clinicId
     * @param visitDate
     * @param visitPeriod
     * @param doctorId
     * @return
     */
    public ClinicQueue getQueueCalledInfo(Long clinicId,LocalDate visitDate,String visitPeriod,Long doctorId){
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        criteria.andClinicIdEqualTo(clinicId);
        criteria.andVisitDateEqualTo(visitDate);
        criteria.andVisitPeriodEqualTo(visitPeriod);
        criteria.andDoctorIdEqualTo(doctorId);
        criteria.andQueueStatusEqualTo(QueueStatusEnum.CALLED.getValue());
        criteria.andIsDeleteEqualTo(YesOrNoEnum.NO.getValue());
        example.setLimit(CommonConstant.DEFAULT_1);
        List<ClinicQueue> clinicQueueList = clinicQueueMapper.selectByExample(example);
        return CollectionUtils.isEmpty(clinicQueueList) ? null : clinicQueueList.get(0);
    }


    /**
     * 呼叫下一位
     * @param clinicQueue
     */
    @Transactional(rollbackFor = Exception.class)
    public void callNext(ClinicQueue clinicQueue){
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if(dbClinicQueue == null){
            throw new BusinessFailException("排队信息不存在");
        }
        if(!QueueStatusEnum.READY.getValue().equals(dbClinicQueue.getQueueStatus())){
            throw new BusinessFailException("状态不允许");
        }
        //将呼叫中的患者设置为过号
        ClinicQueueVo calledQueue = getCalled(dbClinicQueue.getClinicId(),dbClinicQueue.getDoctorId());
        if(calledQueue != null){
            ClinicQueue calledQueueDb = new ClinicQueue();
            calledQueueDb.setQueueId(calledQueue.getQueueId());
            calledQueueDb.setQueueStatus(QueueStatusEnum.MISSED.getValue());
            calledQueueDb.setMissTime(new Date());
            updateQueue(calledQueueDb);
        }
        dbClinicQueue.setQueueStatus(QueueStatusEnum.CALLED.getValue());
        dbClinicQueue.setCallCount(CommonConstant.DEFAULT_1);
        dbClinicQueue.setCallTime(new Date());
        updateQueue(dbClinicQueue);
        //调用设备进行播报
        sendCallMessage(dbClinicQueue);
    }

    /**
     * 发送消息
     * @param clinicQueue
     */
    public void sendCallMessage(ClinicQueue clinicQueue) {
        CallLargeMessage callLargeMessage = new CallLargeMessage();
        callLargeMessage.setClinicId(clinicQueue.getClinicId());
        /**
         * 第一个参数：医生
         * 第二个参数：时段  上午/下午
         * 第三个参数：几号 5号
         * 第三个参数：患者名
         * 第四个参数：几诊室
         *
         */
        callLargeMessage.setMessage(String.format(
                Constants.CALL_MESSAGE,
                clinicQueue.getDoctorName(),
                VisitPeriodEnum.getText(clinicQueue.getVisitPeriod()),
                clinicQueue.getVisitSeqNo(),
                clinicQueue.getPatientName(),
                clinicQueue.getDeptName() + clinicQueue.getRoomName()));
        redisUtil.publish(RedisKeyPrefix.TOPIC_CLINIC_CALL_LARGE, JsonUtil.obj2Json(callLargeMessage));
    }

    /**
     * 重新呼叫
     * @param clinicQueue
     */
    @Transactional(rollbackFor = Exception.class)
    public void repeatCall(ClinicQueue clinicQueue){
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if(dbClinicQueue == null){
            throw new BusinessFailException("排队信息不存在");
        }
        if(!QueueStatusEnum.CALLED.getValue().equals(dbClinicQueue.getQueueStatus())){
            throw new BusinessFailException("状态不允许");
        }
        dbClinicQueue.setCallCount(dbClinicQueue.getCallCount() + CommonConstant.DEFAULT_1);
        dbClinicQueue.setCallTime(new Date());
        updateQueue(dbClinicQueue);
        //调用设备进行播报
        sendCallMessage(dbClinicQueue);
    }

    /**
     * 过号
     * @param clinicQueue
     */
    @Transactional(rollbackFor = Exception.class)
    public void missed(ClinicQueue clinicQueue){
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if(dbClinicQueue == null){
            throw new BusinessFailException("排队信息不存在");
        }
        if(!QueueStatusEnum.CALLED.getValue().equals(dbClinicQueue.getQueueStatus())){
            throw new BusinessFailException("状态不允许");
        }
        dbClinicQueue.setQueueStatus(QueueStatusEnum.MISSED.getValue());
        dbClinicQueue.setMissTime(new Date());
        updateQueue(dbClinicQueue);
        //刷新待诊队列
        refreshReadyQueue(dbClinicQueue.getClinicId(),dbClinicQueue.getVisitDate(),dbClinicQueue.getVisitPeriod(),dbClinicQueue.getDoctorId());
    }

    /**
     * 完成诊断/去检查以后调用
     * @param clinicId
     * @param patientId
     * @param visitNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void treated(Long clinicId,Long patientId,String visitNo){
        ClinicQueue dbClinicQueue = getQueueByVisitNo(clinicId,patientId,visitNo);
        if(dbClinicQueue == null){
            return;
        }
        if(QueueStatusEnum.TREATED.getValue().equals(dbClinicQueue.getQueueStatus())){
            log.info("叫号已经是完诊状态了:{}",visitNo);
            return;
        }
        dbClinicQueue.setQueueStatus(QueueStatusEnum.TREATED.getValue());
        dbClinicQueue.setTreatedTime(new Date());
        updateQueue(dbClinicQueue);
        //刷新待诊队列
        refreshReadyQueue(dbClinicQueue.getClinicId(),dbClinicQueue.getVisitDate(),dbClinicQueue.getVisitPeriod(),dbClinicQueue.getDoctorId());
    }

    /**
     * 根据诊号查询排队信息
     * @param clinicId
     * @param patientId
     * @param visitNo
     * @return
     */
    public ClinicQueue getQueueByVisitNo(Long clinicId,Long patientId,String visitNo){
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        criteria.andClinicIdEqualTo(clinicId);
        criteria.andPatientIdEqualTo(patientId);
        criteria.andVisitNoEqualTo(visitNo);
        List<ClinicQueue> clinicQueueList = clinicQueueMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(clinicQueueList)){
            return null;
        }
        return clinicQueueList.get(0);
    }

    /**
     * 重新排队
     * @param clinicQueue
     */
    @Transactional(rollbackFor = Exception.class)
    public void repeatWait(ClinicQueue clinicQueue){
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if(dbClinicQueue == null){
            throw new BusinessFailException("排队信息不存在");
        }
        if(!QueueStatusEnum.MISSED.getValue().equals(dbClinicQueue.getQueueStatus())){
            throw new BusinessFailException("状态不允许");
        }
        dbClinicQueue.setQueueStatus(QueueStatusEnum.WAITING.getValue());
        dbClinicQueue.setWaitTime(new Date());
        updateQueue(dbClinicQueue);
        //刷新待诊队列
        refreshReadyQueue(dbClinicQueue.getClinicId(),dbClinicQueue.getVisitDate(),dbClinicQueue.getVisitPeriod(),dbClinicQueue.getDoctorId());
    }



    /**
     * 查询候诊中的队列数量
     * @param visitDate
     * @param visitPeriod
     * @param doctorId
     * @return
     */
    public Long getReadyCount(Long clinicId,LocalDate visitDate,String visitPeriod,Long doctorId){
        ClinicQueueExample example = new ClinicQueueExample();
        ClinicQueueExample.Criteria criteria = example.createCriteria();
        criteria.andClinicIdEqualTo(clinicId);
        criteria.andVisitDateEqualTo(visitDate);
        criteria.andQueueStatusEqualTo(QueueStatusEnum.READY.getValue());
        criteria.andVisitPeriodEqualTo(visitPeriod);
        criteria.andDoctorIdEqualTo(doctorId);
        criteria.andIsDeleteEqualTo(YesOrNoEnum.NO.getValue());
        return clinicQueueMapper.countByExample(example);
    }


    /**
     * 校验签到记录
     * @param clinicQueue 签到实体
     */
    public void validateQueue(ClinicQueue clinicQueue) {
        if(StringUtil.isBlank(clinicQueue.getSortNum())){
            clinicQueue.setSortNum(CommonConstant.DEFAULT_1);
        }
    }

    /**
     * 根据主键查询签到记录
     * @param queueId 主键ID
     * @return 查询结果
     */
    public ClinicQueue getQueueById(Long queueId) {
        if (StringUtil.isBlank(queueId)) {
            return null;
        }
        ClinicQueue clinicQueue = clinicQueueMapper.selectByPrimaryKey(queueId);
        if (clinicQueue == null || YesOrNoEnum.YES.getValue().equals(clinicQueue.getIsDelete())) {
            return null;
        }
        return clinicQueue;
    }

    /**
     * 修改签到记录
     * @param clinicQueue 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateQueue(ClinicQueue clinicQueue) {
        clinicQueue.setUpdateTime(new Date());
        clinicQueueMapper.updateByPrimaryKeySelective(clinicQueue);
    }

    /**
     * 删除签到记录（逻辑删除）
     * @param clinicQueue 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteQueue(ClinicQueue clinicQueue) {
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if (dbClinicQueue == null) {
            throw new BusinessFailException("签到记录不存在");
        }
        dbClinicQueue.setIsDelete(YesOrNoEnum.YES.getValue());
        dbClinicQueue.setUpdateUserId(clinicQueue.getUpdateUserId());
        dbClinicQueue.setUpdateBy(clinicQueue.getUpdateBy());
        dbClinicQueue.setUpdateTime(new Date());
        clinicQueueMapper.updateByPrimaryKeySelective(dbClinicQueue);
    }

    /**
     * 启用签到记录
     * @param clinicQueue 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void enableQueue(ClinicQueue clinicQueue) {
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if (dbClinicQueue == null) {
            throw new BusinessFailException("签到记录不存在");
        }
        if (YesOrNoEnum.YES.getValue().equals(dbClinicQueue.getIsEnabled())) {
            return;
        }
        dbClinicQueue.setIsEnabled(YesOrNoEnum.YES.getValue());
        dbClinicQueue.setUpdateUserId(clinicQueue.getUpdateUserId());
        dbClinicQueue.setUpdateBy(clinicQueue.getUpdateBy());
        dbClinicQueue.setUpdateTime(new Date());
        clinicQueueMapper.updateByPrimaryKeySelective(dbClinicQueue);
    }

    /**
     * 停用签到记录
     * @param clinicQueue 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void disableQueue(ClinicQueue clinicQueue) {
        ClinicQueue dbClinicQueue = getQueueById(clinicQueue.getQueueId());
        if (dbClinicQueue == null) {
            throw new BusinessFailException("签到记录不存在");
        }
        if (YesOrNoEnum.NO.getValue().equals(dbClinicQueue.getIsEnabled())) {
            return;
        }
        dbClinicQueue.setIsEnabled(YesOrNoEnum.NO.getValue());
        dbClinicQueue.setUpdateUserId(clinicQueue.getUpdateUserId());
        dbClinicQueue.setUpdateBy(clinicQueue.getUpdateBy());
        dbClinicQueue.setUpdateTime(new Date());
        clinicQueueMapper.updateByPrimaryKeySelective(dbClinicQueue);
    }
}
